#pragma once
#include "nnse_support.h"

namespace nnse
{
	template <class T, class U> public ref class neuron abstract
	{
		// Constructor and Destructor
			public: neuron(layer<T, U>^ p, unsigned int n_n);
			public: virtual ~neuron();
		// Member Variables and their Accessor Functions
			// parent - the layer to which this neuron belongs
				private: layer<T, U>^ parent;
				public: problem<T, U>^ get_parent_problem();
				public: solver<T, U>^ get_parent_solver();
				public: MLP<T, U>^ get_parent_MLP();
				public: layer<T, U>^ get_parent_layer();
			// number - the neuron's number relative to the other neurons in the parent layer
				private: unsigned int number;
				public: unsigned int get_number();
				protected: void put_number(unsigned int n);
			// NET - the sum of the output signals from the neuron's dendrites
				protected: U NET;
				public: U get_NET();
				protected: void put_NET();
			// OUT - the neuron's output (NET through a non-linear threshold function)
				protected: U OUT;
				public: U get_OUT();
				protected: void put_OUT();
			// ERR - the sum of the errors propogated back from the output layer
				protected: U ERR;
				public: U get_ERR();
				protected: void put_ERR(layer<T, U>% next_layer);
			// DELTA - the neuron's error (ERR through a squashing function)
				protected: U DELTA;
				public: U get_DELTA();
				protected: void put_DELTA();
			// number_of_dendrites - the number of dendrites in this neuron
				private: unsigned int number_of_dendrites;
				public: unsigned int get_number_of_dendrites();
				protected: void put_number_of_dendrites(unsigned int n_d);
			// dendrites - the dendrites in this neuron
				private: array<dendrite<T, U>^>^ dendrites;
				public: dendrite<T, U>% get_dendrite(unsigned int d_n);
				protected: void put_dendrites();
			// neuron_form - the neuron's user interface
				protected: SimpleGUI::Neuron<T, U>^ neuron_form;
		// Worker Functions
			public: virtual void forward(layer<T, U>% next_layer, bool update_flag);
			public: virtual U reverse(layer<T, U>% next_layer);
			protected: virtual U calc_NET();
			protected: virtual U calc_OUT();
			protected: virtual U calc_ERR(layer<T, U>% next_layer);
			protected: virtual U calc_DELTA();
			protected: void update_fanout_weights(layer<T, U>% next_layer);
			protected: void update_fanin_weight(unsigned int d_n, U OUT);
			protected: void pass_on_OUT(layer<T, U>% next_layer);
			protected: void filter_back_DELTA();
		// GUI Functions
			public: void display_form();
			public: void invalidate_form();
			public: void display_dendrites();
		// Serialisation Functions
			public: virtual void read(std::wifstream& in);
			public: virtual void write(std::wofstream& out);
			public: virtual void display(std::wofstream& out);
	};
	template <class T, class U> std::wifstream& operator>>(std::wifstream& in, neuron<T, U>% n);
	template <class T, class U> std::wofstream& operator<<(std::wofstream& out, neuron<T, U>% n);
}
